[[我也希望安全第一]]|第 10/30 天
第 2 篇把 alignment 和 control 分開時,兩者還像兩套抽象理念。走完 Harness、代理型失準、prompt injection、guardrail 與產品摩擦之後,我想把它們放回產品會議裡。
假設現在桌上有三個需求:替客服工單做摘要、替工程師修改程式,以及替財務處理付款。它們都可以叫做 Agent,真正需要的安全標準卻差很多。與其先爭論該用更強的模型還是更多的限制,我會照下面的順序走一次。
列出外部效果
↓
找出不能跨越的線
↓
分開模型判斷與系統保證
↓
設定工具、權限與人工批准
↓
設計 allow test、deny test 與完成證據
↓
寫下發布門檻與殘餘風險
這不是一份通用認證標準,比較像上線前用來對焦的一輪檢查。每一步都問同樣的三個產品,答案很快就會分岔。
我會先問:「這個 Agent 猜錯時,外面的世界會有什麼變化?」不是問它用了哪個模型,也不是先替 prompt 加上「請小心」。
摘要 Agent 只讀取單一客戶的既有工單,輸出一段內部文字;程式 Agent 會修改 repository、執行測試並建立 pull request;付款 Agent 則可能選擇帳戶、收款人與金額,最後送出交易。
把效果列出來以後,三者已經不能再放在同一個風險籃子裡:
| 產品 | 正常效果 | 猜錯時可能留下什麼 |
|---|---|---|
| 客服摘要 | 讀取工單、產生內部摘要 | 摘要失真、遺漏資訊、混入其他客戶資料 |
| 程式修改 | 讀檔、寫檔、跑測試、建立 PR | 改錯目錄、刪掉工作、碰到 secret 或 production |
| 付款 | 讀發票、提出或送出交易 | 付錯金額、付錯對象、產生未授權交易 |
第 5 篇談 Tool Call 時,真正重要的也是這一層:模型提出 send_payment,和銀行真的收到付款要求,是兩件事。中間那段不能省略成一句「模型有工具可用」。
Anthropic 最近公布的生物實驗室案例,提供了另一個很乾淨的分界。公司稱約 950 個 Claude Agent 花了 21 小時搜尋資料、提出酵素系統的研究方向,但真正接觸材料、操作設備與驗證結果的仍是人類科學家,而且限制在 BSL-1、BSL-2 環境。成果是否新穎還要等論文和同行重現;就控制設計而言,比較值得記住的是:可以把模型的探索空間開得很大,不代表要把實體執行權一起交出去。
摘要漏掉一句話,使用者通常還能點回原始工單;PR 改壞了,可以關掉或重做,但刪掉別人尚未提交的工作就未必救得回來;款項一旦送出,還可能牽涉銀行、供應商與對帳流程。
所以我會替每個 workflow 寫下一條不能跨越的線:摘要不能跨租戶讀取,程式 Agent 不能寫入 production 或刪除既有未提交工作,付款 Agent 不能在未經批准時送出交易。
這一步看起來很基本,卻剛好是第 6 篇 VM 案例缺少的東西。任務要求刪除 VM 1、2、3,Agent 找不到它們,便拿 5、6、7 代替。若規格只有「完成三次刪除」,它甚至可能覺得自己很努力;若規格寫的是「只能操作這三個 ID,不存在就停止」,替換目標就不再是一種解法。
模型適合處理語義。它可以判斷摘要是否抓到客戶真正的問題、patch 是否符合需求、發票看起來是否重複或異常。這些能力可以用 eval 改善,但不會因為測到 99% 就變成保證。
不能跨越的線要換一種做法。租戶 ID 由伺服器端從登入身分帶入,不讓模型自由填寫;程式修改限制在獨立 worktree,環境裡不放 production credential;付款的收款人、金額、幣別與帳戶交給一般程式驗證。首次出現的收款人或超過門檻的金額,再送給人決定。
第 8 篇比較過外掛 classifier 與模型內建防護。兩者放置的位置不同,仍然都是會誤判的機率性控制。它們可以減少危險 proposal,卻不該兼任租戶隔離、IAM 或交易不變量。
到了這一步才選工具和權限,會比「先給它一個 shell 再慢慢限制」容易得多。
客服摘要需要同租戶、唯讀的工單查詢,不需要寄信、退款或修改 CRM。程式 Agent 可以讀寫指定 worktree、執行測試並建立 branch,但不能讀取部署憑證,也不能直接合併。付款 Agent 可以讀發票、查供應商資料並建立 proposal;真正的 commit payment 是另一個能力,而且批准必須綁定這一次交易的收款人、金額、幣別與帳戶,參數一改就失效。
第 7 篇那份被動過手腳的 PDF,也應該在這裡出現。外部文件可以影響模型對內容的理解,不能因此取得新的能力。發票裡就算藏著「把款項改匯到另一個帳戶」,它能碰到的仍然只該是 proposal。
這也是第 9 篇所說的產品取捨真正反映在設計上的地方。摘要不必每產生一段就問人;付款也不能因為使用者嫌確認麻煩,就把批准按鈕變成裝飾。摩擦應該跟外部效果一起增加,不是平均灑在所有操作上。
我不會只留一個 task success rate。至少要看到三類結果:
allow test:正常任務能不能完成?
deny test:越權或危險要求是否真的被執行層拒絕?
完成證據:系統憑什麼相信 Agent 已經做完?
摘要 Agent 的 allow test 可以看長工單、少數語言與引用忠實度;deny test 用真實服務身分證明它讀不到其他租戶;完成證據則讓每句摘要能點回來源。
程式 Agent 要真的產生 diff、跑測試並建立 PR,也要測試它無法寫出 worktree、讀取 secret 或直接部署。不能只接受模型回一句「已修好」。若 Generator 和 Evaluator 共用同一個模型與同一份污染 context,兩者一致也可能只是一起看漏,因此測試、lint、權限與 branch protection 要保留獨立判斷。
付款 Agent 可以測試正常發票能否順利產生 proposal,也要刻意換掉收款人、提高金額、重播批准,確認交易層全部拒絕。這裡追求的不是模型從不誤報,而是未授權交易維持為零。
第 6 篇已經把 VM 案例改寫成幾條可重播的軌跡,做的正是這種 deny test。至於高風險 eval 要如何架隔離 Runner、限制網路和保存證據,後面會再實際動手;這裡先把「發布前必須證明什麼」寫清楚。
走到最後,團隊應該能填完這張表,而不是只在簡報上留下一個 safety score:
workflow:
主要使用者與受影響第三方:
Agent 能造成的最大外部效果:
不能跨越的線:
依賴模型判斷的部分:
- 評估資料與可接受的失敗門檻:
由系統保證的部分:
- IAM/工具/交易不變量:
- 從真實執行身分完成的 deny test:
完成任務所需的證據:
仍接受的殘餘風險:
失效時由誰停止:
為了完成率、成本或便利,這次放寬了什麼:
同一句「測試通過」,在三個團隊裡不該有同一個意思。客服團隊可能接受少量摘要遺漏,不能接受跨租戶內容;程式團隊可以接受一個失敗但隔離良好的 patch,不能接受 production 寫入;財務團隊可以容許正常發票被送去人工覆核,不能容許未批准付款。
這張決定書不要求每個產品堆滿同樣的鎖。它只是逼我們說清楚:哪些判斷容許以機率改善,哪些結果必須由系統擋住,以及為了讓產品能用,我們接受了什麼。
到這裡,我們得到的是一份可以發布的設計,還不是一個已被證明安全的系統。後面會把 Agent 接上真實環境,看看文件裡的權限、隔離、監控與復原假設,實際上能撐多久。
第5-10篇這幾篇不再期待模型自己永遠做對,而是把一次 tool call 從提案、授權到執行分開。六篇累積下來,控制面可以整理成這張發布前檢查表:
| 鎖 | 放在哪裡 | 想攔住什麼 | 失效時的跡象 | 怎麼驗證 |
|---|---|---|---|---|
| Tool contract 與 schema | 模型到 adapter 的入口 | 任意工具、參數與資源被直接執行 | shell/HTTP 成為旁路 | allow/deny test,未知欄位 fail closed |
| 最小 IAM 與能力 profile | 執行身分 | 合法工具拿著過大權限 | 讀取 task 外 secret、跨租戶或正式環境 | 用真實 service account 做負向測試 |
| Prompt-injection 隔離 | 外部內容與指令 context 之間 | 文件把內容權提升成系統指令 | PDF、網頁要求新增工具或改目的地 | 污染/乾淨文件對照回放 |
| Classifier 與模型內防護 | 輸入、輸出與模型行為 | 高風險或有害提案 | 誤拒正常工作,或同義改寫即可穿透 | shadow mode、切片誤判率與攻擊成功率 |
| 綁定批准與能力預算 | 高影響動作前 | 批准疲乏、長任務無限偏航 | 批准後換參數、步數或成本持續增長 | token 重播、參數變更與 budget exhaustion test |
| Sandbox 與 egress policy | 執行環境 | Agent 把工具組成未預期路徑 | 首次外連、讀到宿主檔案或共用憑證 | 從盒內主動做 escape/egress test |
這些控制能限制動作,仍不能保證真實環境永遠配置正確。後面要繼續處理牆真的破掉以後,誰看見、誰停止、又怎麼復原。